/*
*	Copyright (C) 2014  Julio Montes, Mario Carrillo
*
*	Julio Montes e-mail: imc.coder@gmail.com
*
*	Mario Carrillo e-mail: mario_90ago@live.com.mx
*
*	BSD 3-Clause License
*/

#ifndef SDK_FILECFG_H
#define SDK_FILECFG_H

#include "tinyxml2.h"
using namespace tinyxml2;

#include <wx/wx.h>
#include <vector>
#include <map>

#include "sdk/include/util.h"

namespace sdk
{
    typedef UtilMap<wxString, wxString> mapAttributes;

    typedef std::multimap<wxString, wxString> multimapAttributes;

    struct Element; //definition, necessary for next typedef

    typedef std::multimap<const wxString, struct Element>  multimapElements;

    typedef struct Element
    {
        wxString m_name;
        mapAttributes m_mapAttributes;
        multimapElements m_multimapChilden;
    }Element;

    typedef struct SimpleElement
    {
        wxString m_name;
        mapAttributes m_mapAttributes;
    }SimpleElement;

    typedef std::multimap<wxString, mapAttributes>  multimapSimpleElements;

    /**
    * @brief useful class to read and write XML configuration files
    */
    class FileConfig
    {
    public:
        FileConfig();

        virtual ~FileConfig();

        /**
        * @brief load a XML file
        *
        * if the file not exist, creates a new file which contains a declaration XML, this is only if the parameter @a create is true
        * otherwise, a message of error is displayed. \n
        * if the file has a syntax error, a message with the information of the error is displayed
        *
        * @param filename filename of the file to load
        * @param create if the file not exist and this is true, a new file is created,
        * if the file exist, not is overwrites the contents
        *
        * @return true if the file has been loaded correctly or created a new, otherwise false
        */
        bool LoadFile( const wxString &filename, bool create=true );

        /**
        * @brief returns the filename of the current XML file loaded
        *
        * @return the filename of the current XML file loaded, empty string if not has been loaded a XML file
        */
        const wxString &GetFileName() const;

        /**
        * @brief Save all changes in the file
        * @return true if all changes has been saved correctly in the file, otherwise false
        */
        bool Save();



        wxString ReadString( const wxString &element, const wxString &defaultValue, const wxString &attribute=wxT( "value" ) );

        inline bool ReadBool( const wxString &element, bool defaultValue, const wxString &attribute=wxT( "value" ) )
        {
            return ( ReadString( element, ( defaultValue ? wxT( "1" ) : wxT( "0" ) ), attribute ) != wxT( "0" ) ? true : false );
        }

        inline int ReadInt( const wxString &element, int defaultValue, const wxString &attribute=wxT( "value" ) )
        {
            long value;
            ReadString( element, wxString::Format( wxT( "%d" ), defaultValue ), attribute ).ToLong( &value );
            return value;
        }

        inline double ReadDouble( const wxString &element, double defaultValue, const wxString &attribute=wxT( "value" ) )
        {
            double value;
            ReadString( element, wxString::Format( wxT( "%f" ), defaultValue ), attribute ).ToDouble( &value );
            return value;
        }

        inline wxColour ReadColour( const wxString &element, const wxColour &defaultValue, const wxString &attribute=wxT( "value" ) )
        {
            wxColour color( ReadString( element, defaultValue.GetAsString( wxC2S_HTML_SYNTAX ), attribute ) );
            if( color.IsOk() )
                return color;

            return defaultValue;
        }


		//@old ReadStringAttribute
        wxString ReadString( const wxString &element, const wxString &attribute=wxT( "value" ), size_t match=0 ) const;

        inline bool ReadBool( const wxString &element, const wxString &attribute=wxT( "value" ), size_t match=0 ) const
        {
            return ( ReadString( element, attribute, match ) != wxT( "0" ) ? true : false );
        }

        inline int ReadInt( const wxString &element, const wxString &attribute=wxT( "value" ), size_t match=0 ) const
        {
            long value=0;
            ReadString( element, attribute, match ).ToLong( &value );
            return value;
        }

        inline double ReadDouble( const wxString &element, const wxString &attribute=wxT( "value" ), size_t match=0 ) const
        {
            double value=0;
            ReadString( element, attribute, match ).ToDouble( &value );
            return value;
        }

        inline wxColour ReadColour( const wxString &element, const wxString &attribute=wxT( "value" ), size_t match=0 ) const
        {
            wxColour color( ReadString( element, attribute, match ) );
            if( color.IsOk() )
                return color;

            return wxNullColour;
        }





        /**
        * @brief Sets attribute value of element
        *
        * if position of element not match with 'match' parameter, attribute values is set to first
        * occurrence of the element, if the element not exist then it is created
        */
        size_t WriteString( const wxString &element, const wxString &value, const wxString &attribute=wxT( "value" ), size_t match=0 );

        inline size_t WriteBool( const wxString &element, bool value, const wxString &attribute=wxT( "value" ), size_t match=0 )
        {
            return WriteString( element, ( value ? wxT( "1" ) : wxT( "0" ) ), attribute, match );
        }

        inline size_t WriteInt( const wxString &element, int value, const wxString &attribute=wxT( "value" ), size_t match=0 )
        {
            return WriteString( element, wxString::Format( wxT( "%d" ), value ), attribute, match );
        }

        inline size_t WriteDouble( const wxString &element, double value, const wxString &attribute=wxT( "value" ), size_t match=0 )
        {
            return WriteString( element, wxString::Format( wxT( "%f" ), value ), attribute, match );
        }

        inline size_t WriteColour( const wxString &element, const wxColour &value, const wxString &attribute=wxT( "value" ), size_t match=0 )
        {
            return WriteString( element, value.GetAsString( wxC2S_HTML_SYNTAX ), attribute, match );
        }



        /**
        * @return the position where the new element has been inserted
        */
        size_t WriteNewString( const wxString &element, const wxString &value, const wxString &attribute=wxT( "value" ) );

        inline size_t WriteNewBool( const wxString &element, bool value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewString( element, ( value ? wxT( "1" ) : wxT( "0" ) ), attribute );
        }

        inline size_t WriteNewInt( const wxString &element, int value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewString( element, wxString::Format( wxT( "%d" ), value ), attribute );
        }

        inline size_t WriteNewDouble( const wxString &element, double value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewString( element, wxString::Format( wxT( "%f" ), value ), attribute );
        }

        inline size_t WriteNewColour( const wxString &element, const wxColour &value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewString( element, value.GetAsString( wxC2S_HTML_SYNTAX ), attribute );
        }



        /**
        * @return the position where @ref nameNewElement has been inserted, -1 if the new element not has been inserted
        */
		//@old WriteNewStringInElement
        int WriteNewStringElement( const wxString &element, size_t match, const wxString &nameNewElement, const wxString &value, const wxString &attribute=wxT( "value" ) );

        inline int WriteNewBoolElement( const wxString &element, size_t match, const wxString &nameNewElement, bool value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewStringElement( element, match, nameNewElement, ( value ? wxT( "1" ) : wxT( "0" ) ), attribute );
        }

        inline int WriteNewIntElement( const wxString &element, size_t match, const wxString &nameNewElement, int value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewStringElement( element, match, nameNewElement, wxString::Format( wxT( "%d" ), value ), attribute );
        }

        inline int WriteNewDoubleElement( const wxString &element, size_t match, const wxString &nameNewElement, double value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewStringElement( element, match, nameNewElement, wxString::Format( wxT( "%f" ), value ), attribute );
        }

        inline int WriteNewColourElement( const wxString &element, size_t match, const wxString &nameNewElement, const wxColour &value, const wxString &attribute=wxT( "value" ) )
        {
            return WriteNewStringElement( element, match, nameNewElement, value.GetAsString( wxC2S_HTML_SYNTAX ), attribute );
        }



        /** @return the number of attributes read, -1 if @ref element was not found*/
        int ReadAttributes( const wxString &element, mapAttributes *mAttributes, size_t match=0 ) const;


        /**
        * @return the position where @ref element has been inserted, -1 if the new element not has been inserted
        */
		//@old WriteAttributesInElement
        int WriteNewElement( const wxString &parentElement, size_t match, const Element &element );

        int WriteNewElements( const wxString &parentElement, size_t match, const multimapElements &elements );

        int WriteNewElement( const wxString &parentElement, size_t match, const SimpleElement &element );

        int WriteNewElements( const wxString &parentElement, size_t match, const multimapSimpleElements &elements );

        //@old WriteAttributes
        void WriteElement( const wxString &element, const mapAttributes &mAttributes );

        //@old WriteNewAttributes
        void WriteNewElement( const wxString &element, const mapAttributes &mAttributes );


        size_t ReadElements( const wxString &element, std::vector<Element> *vectorElements ) const;


        //int ReadChildren( const wxString &element, multimapElements *mmElements, size_t match=0 ) const;


        bool DeleteElement( const wxString &element, size_t match=0 );

        size_t DeleteAllElements( const wxString &element );


        XMLElement *FindElement( const wxString &element, size_t match=0 ) const;
    protected:
    private:
        void ReadChildren( const XMLElement *xmlElement, Element *childElement ) const;
        XMLElement *WriteChildren( XMLElement *xmlElement, const Element &childElement ) const;
        XMLElement *WriteChildren( XMLElement *xmlElement, const SimpleElement &childElement ) const;

        XMLElement *InsertElement( const wxString &element );
        XMLElement *InsertNewElement( const wxString &element, size_t *position=NULL );

        wxString m_fileName;
        XMLDocument *m_file;
    };
}

#endif // SDK_FILECFG_H
